package com.gitee.wsl.transform.encode.pinyin.trie

import com.gitee.wsl.transform.encode.pinyin.trie.Payload
import kotlin.jvm.JvmOverloads

/**
 *
 *
 * A state has various important tasks it must attend to:
 *
 *
 *  * success; when a character points to another state, it must return that
 * state
 *  * failure; when a character has no matching state, the algorithm must be
 * able to fall back on a state with less depth
 *  * emits; when this state is passed and keywords have been matched, the
 * matches and their payloads must be 'emitted' so that they can be used later
 * on.
 *
 *
 *
 * The root state is special in the sense that it has no failure state; it
 * cannot fail. If it 'fails' it will still parse the next character and start
 * from the root node. This ensures that the algorithm always runs. All other
 * states always have a fail state.
 *
 *
 * @author Daniel Beck
 */
class PayloadState<T> @JvmOverloads constructor(
    /**
     * effective the size of the keyword
     */
    val depth: Int = 0
) {
    /**
     * only used for the root state to refer to itself in case no matches have been
     * found
     */
    private val rootState: PayloadState<T>? = if (depth == 0) this else null

    /**
     * referred to in the white paper as the 'goto' structure. From a state it is
     * possible to go to other states, depending on the character passed.
     */
    private val success = HashMap<Char, PayloadState<T>>()

    /**
     * if no matching states are found, the failure state will be returned
     */
    var failure: PayloadState<T>? = null

    /**
     * whenever this state is reached, it will emit the matches keywords for future
     * reference
     */
    private var emits: MutableSet<Payload<T>>? = null

    private fun nextState(character: Char, ignoreRootState: Boolean): PayloadState<T>? {
        var nextState = this.success[character]
        if (!ignoreRootState && nextState == null && this.rootState != null) {
            nextState = this.rootState
        }
        return nextState
    }

    fun nextState(character: Char): PayloadState<T>? {
        return nextState(character, false)
    }

    fun nextStateIgnoreRootState(character: Char): PayloadState<T>? {
        return nextState(character, true)
    }

    fun addState(character: Char): PayloadState<T> {
        var nextState = nextStateIgnoreRootState(character)
        if (nextState == null) {
            nextState = PayloadState(this.depth + 1)
            this.success.put(character, nextState)
        }
        return nextState
    }

    /**
     * Adds a payload to be emitted for this state.
     *
     * @param payload to be emitted.
     */
    fun addEmit(payload: Payload<T>) {
        if (this.emits == null) {
            this.emits = mutableSetOf()
        }
        this.emits!!.add(payload)
    }

    /**
     * Adds a collection of payloads to be emitted for this state.
     *
     * @param emits Collection of payloads to be emitted.
     */
    fun addEmit(emits: Collection<Payload<T>>) {
        for (emit in emits) {
            addEmit(emit)
        }
    }

    /**
     * Returns a collection of emitted payloads for this state.
     *
     * @return Collection of emitted payloads.
     */
    fun emit(): Collection<Payload<T>> {
        return this.emits?:emptyList()
    }

//    fun failure(): PayloadState<T>? {
//        return this.failure
//    }
//
//    fun setFailure(failState: PayloadState<T>?) {
//        this.failure = failState
//    }

    val states: Collection<PayloadState<T>>
        get() = this.success.values

    val transitions: Collection<Char>
        get() = this.success.keys
}